/*!
    \copyright  Copyright (c) 2021 - 2024 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \ingroup    le_unicast_manager
    \brief      Implementation of the media control interface for LE music sources.
*/
#if defined(INCLUDE_LE_AUDIO_UNICAST)

#include "le_unicast_manager.h"
#include "le_unicast_manager_instance.h"
#include "le_unicast_manager_private.h"
#include "le_unicast_media_control_interface.h"

#include "audio_sources.h"
#include "audio_sources_media_control_interface.h"
#include "multidevice.h"
#include "gatt_pacs_server.h"
#include "bt_device.h"
#include "gatt_connect.h"
#ifdef USE_SYNERGY
#include "media_control_client.h"
#endif
#include "gatt.h"

#include <logging.h>
#include <panic.h>
#include <stdlib.h>

static unsigned leUnicastMusicSource_GetContext(audio_source_t source);

#ifdef USE_SYNERGY

static void leUnicastManagerMediaControl_Play(audio_source_t source);
static void leUnicastManagerMediaControl_Pause(audio_source_t source);
static void leUnicastManagerMediaControl_PlayPause(audio_source_t source);
static void leUnicastManagerMediaControl_Stop(audio_source_t source);
static void leUnicastManagerMediaControl_Forward(audio_source_t source);
static void leUnicastManagerMediaControl_Backward(audio_source_t source);
static void leUnicastManagerMediaControl_FastForward(audio_source_t source, bool state);
static void leUnicastManagerMediaControl_FastRewind(audio_source_t source, bool state);
#define leUnicastManagerMediaControl_Device leUnicastManager_GetBtAudioDevice

#else

#define leUnicastManagerMediaControl_Play               NULL
#define leUnicastManagerMediaControl_Pause              NULL
#define leUnicastManagerMediaControl_PlayPause          NULL
#define leUnicastManagerMediaControl_Stop               NULL
#define leUnicastManagerMediaControl_Forward            NULL
#define leUnicastManagerMediaControl_Backward           NULL
#define leUnicastManagerMediaControl_FastForward        NULL
#define leUnicastManagerMediaControl_FastRewind         NULL
#define leUnicastManagerMediaControl_Device             NULL

#endif

static const media_control_interface_t le_unicast_source_media_control_interface = {
    .Play = leUnicastManagerMediaControl_Play,
    .Pause = leUnicastManagerMediaControl_Pause,
    .PlayPause = leUnicastManagerMediaControl_PlayPause,
    .Stop = leUnicastManagerMediaControl_Stop,
    .Forward = leUnicastManagerMediaControl_Forward,
    .Back = leUnicastManagerMediaControl_Backward,
    .FastForward = leUnicastManagerMediaControl_FastForward,
    .FastRewind = leUnicastManagerMediaControl_FastRewind,
    .NextGroup = NULL,
    .PreviousGroup = NULL,
    .Shuffle = NULL,
    .Repeat = NULL,
    .Context = leUnicastMusicSource_GetContext,
    .Device = leUnicastManagerMediaControl_Device,
};

/*! \brief Table which maps 'preferred' context to use for different usecases. Note that these contexts will be used
           only if the usecase is currently streaming. */
static const audio_source_provider_context_t le_um_media_usecase_audio_context_map_table[] = {
    [le_um_media_usecase_media] = context_audio_is_streaming,
    [le_um_media_usecase_gaming_with_vbc] = context_audio_is_playing_game_vbc,
    [le_um_media_usecase_live] = context_audio_is_recording,
    [le_um_media_usecase_emergency_alarm] = context_audio_is_alarm,
    [le_um_media_usecase_game] = context_audio_is_playing_game_no_vbc,
    [le_um_media_usecase_instructional] = context_audio_is_streaming,
    [le_um_media_usecase_sound_effects] = context_audio_is_streaming,
    [le_um_media_usecase_notifications] = context_audio_is_streaming,
    [le_um_media_usecase_alerts] = context_audio_is_streaming,
};

static bool leUnicastManagerMediaControl_IsStreamingForMedia(le_um_instance_t *inst)
{
    return (LeUnicastManager_IsContextOfTypeMedia(inst->audio_context) && LeUnicastManager_IsAnyAseEnabled(inst));
}

/*! \brief Get the usecase compatible with the given audio context */
static le_um_media_usecase_t leUnicastManager_GetUsecaseFromAudioContext(le_um_instance_t *inst, AudioContextType audio_context)
{
    le_um_media_usecase_t usecase = le_um_media_usecase_max;

    switch (audio_context)
    {
        case AUDIO_CONTEXT_TYPE_MEDIA:
            usecase = le_um_media_usecase_media;
        break;

        case AUDIO_CONTEXT_TYPE_GAME:
        {
            /* Check if any source ASE is enabled or not */
            if (leUnicastManager_IsAnyAseEnabledForGivenDirection(inst, ASE_DIRECTION_AUDIO_SOURCE))
            {
                usecase = le_um_media_usecase_gaming_with_vbc;
            }
            else
            {
                usecase = le_um_media_usecase_game;
            }
        }
        break;

        case AUDIO_CONTEXT_TYPE_INSTRUCTIONAL:
            usecase = le_um_media_usecase_instructional;
        break;

        case AUDIO_CONTEXT_TYPE_LIVE:
            usecase = le_um_media_usecase_live;
        break;

        case AUDIO_CONTEXT_TYPE_SOUND_EFFECTS:
            usecase = le_um_media_usecase_sound_effects;
        break;

        case AUDIO_CONTEXT_TYPE_NOTIFICATIONS:
            usecase = le_um_media_usecase_notifications;
        break;

        case AUDIO_CONTEXT_TYPE_ALERTS:
            usecase = le_um_media_usecase_alerts;
        break;

        case AUDIO_CONTEXT_TYPE_EMERGENCY_ALARM:
            usecase = le_um_media_usecase_emergency_alarm;
        break;

        case AUDIO_CONTEXT_TYPE_UNSPECIFIED:
        case AUDIO_CONTEXT_TYPE_VOICE_ASSISTANT:
        case AUDIO_CONTEXT_TYPE_RINGTONE:
            /* As of now, keeping its same as usecase notifications */
            usecase = le_um_media_usecase_notifications;
        break;

        default:
            /* Not supported usecase */
            UNICAST_MANAGER_WARN("leUnicastManager_GetUsecaseFromAudioContext not supported audio context %d", audio_context);
        break;
    }

    return usecase;
}

/*! \brief Get the context to use for given audio context which is streaming currently */
static unsigned leUnicastMusicSource_GetAudioSrcContextForCurrentUsecase(le_um_instance_t *inst)
{
    audio_source_provider_context_t context = context_audio_disconnected;
    le_um_media_usecase_t usecase;

    usecase = leUnicastManager_GetUsecaseFromAudioContext(inst, inst->audio_context);

    if (usecase < le_um_media_usecase_max)
    {
        context = le_um_media_usecase_audio_context_map_table[usecase];
    }

    return context;
}

static unsigned leUnicastMusicSource_GetContext(audio_source_t source)
{
    audio_source_provider_context_t context = context_audio_disconnected;
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByAudioSource(source);

    if (inst != NULL)
    {
        {
            /* Use audio-source-context from MCP.
             * If it says not playing and ASEs is in streaming state, then use audio-src-context from the usecase table
             */
            context = MediaClientControl_GetAudioSourceContext(inst->cid);
            if (context != context_audio_is_playing && leUnicastManagerMediaControl_IsStreamingForMedia(inst))
            {
                context = leUnicastMusicSource_GetAudioSrcContextForCurrentUsecase(inst);
            }

            /* Sometimes we see remote keeps the media state as "Playing" after disabling all the ASE's. In such a scenario,
             * if we returns the context as playing, audio router will not give focus to any background/low priority streams. 
             * This check is added to ensure the other low priority sources gets focus.
             * OR if the streaming is not for media, return the context as connected.
             */
            if ((context == context_audio_is_playing || context == context_audio_is_paused) && !LeUnicastManager_IsAnyAseEnabled(inst))
            {
                context = context_audio_connected;
            }
            else if (context != context_audio_disconnected && !leUnicastManagerMediaControl_IsStreamingForMedia(inst))
            {
                context = context_audio_connected;
            }

            UNICAST_MANAGER_LOG("leUnicastMusicSource_GetContext audio_context:%d enum:audio_source_provider_context_t:%d enum:audio_source_t:%d ",
                             inst->audio_context, context, source);
        }
    }

    return (unsigned) context;
}

void LeUnicastMediaControlInterface_Init(void)
{
    UNICAST_MANAGER_LOG("LeUnicastMediaControlInterface_Init");
    AudioSources_RegisterMediaControlInterface(audio_source_le_audio_unicast_1, &le_unicast_source_media_control_interface);
    AudioSources_RegisterMediaControlInterface(audio_source_le_audio_unicast_2, &le_unicast_source_media_control_interface);
}

#ifdef USE_SYNERGY
static void leUnicastManagerMediaControl_Play(audio_source_t source)
{
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByAudioSource(source);

    if (inst)
    {
        MediaControlClient_SendMediaControlOpcode(inst->cid, GATT_MCS_CLIENT_PLAY, 0);
        UNICAST_MANAGER_LOG("leUnicastManagerMediaControl_Play: source=enum:audio_source_t:%d, cid=0x%x", source, inst->cid);
    }
}

static void leUnicastManagerMediaControl_Pause(audio_source_t source)
{
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByAudioSource(source);

    if (inst)
    {
        MediaControlClient_SendMediaControlOpcode(inst->cid, GATT_MCS_CLIENT_PAUSE, 0);
        UNICAST_MANAGER_LOG("leUnicastManagerMediaControl_Pause: source=enum:audio_source_t:%d, cid=0x%x", source, inst->cid);
    }
}

static void leUnicastManagerMediaControl_PlayPause(audio_source_t source)
{
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByAudioSource(source);

    if (inst)
    {
        UNICAST_MANAGER_LOG("leUnicastManagerMediaControl_PlayPause: source=enum:audio_source_t:%d, cid=0x%x", source, inst->cid);
        MediaControlClient_TogglePlayPause(inst->cid);
    }
}

static void leUnicastManagerMediaControl_Stop(audio_source_t source)
{
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByAudioSource(source);

    if (inst)
    {
        MediaControlClient_SendMediaControlOpcode(inst->cid, GATT_MCS_CLIENT_STOP, 0);
        UNICAST_MANAGER_LOG("leUnicastManagerMediaControl_Stop: source=enum:audio_source_t:%d, cid=0x%x", source, inst->cid);
    }
}

static void leUnicastManagerMediaControl_Forward(audio_source_t source)
{
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByAudioSource(source);

    if (inst)
    {
        MediaControlClient_SendMediaControlOpcode(inst->cid, GATT_MCS_CLIENT_NEXT_TRACK, 0);
        UNICAST_MANAGER_LOG("leUnicastManagerMediaControl_Forward: source=enum:audio_source_t:%d, cid=0x%x", source, inst->cid);
    }
}

static void leUnicastManagerMediaControl_Backward(audio_source_t source)
{
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByAudioSource(source);

    if (inst)
    {
        MediaControlClient_SendMediaControlOpcode(inst->cid, GATT_MCS_CLIENT_PREVIOUS_TRACK, 0);
        UNICAST_MANAGER_LOG("leUnicastManagerMediaControl_Backward: source=enum:audio_source_t:%d, cid=0x%x", source, inst->cid);
    }
}

static void leUnicastManagerMediaControl_FastForward(audio_source_t source, bool state)
{
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByAudioSource(source);

    if (inst)
    {
        MediaControlClient_SendMediaControlOpcode(inst->cid, state ? GATT_MCS_CLIENT_FAST_FORWARD : GATT_MCS_CLIENT_STOP, 0);
        UNICAST_MANAGER_LOG("leUnicastManagerMediaControl_FastForward: source=enum:audio_source_t:%d, cid=0x%x state=%u", source, inst->cid, state);
    }
}

static void leUnicastManagerMediaControl_FastRewind(audio_source_t source, bool state)
{
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByAudioSource(source);

    if (inst)
    {
        MediaControlClient_SendMediaControlOpcode(inst->cid, state ? GATT_MCS_CLIENT_FAST_REWIND : GATT_MCS_CLIENT_STOP, 0);
        UNICAST_MANAGER_LOG("leUnicastManagerMediaControl_FastRewind: source=enum:audio_source_t:%d, cid=0x%x state=%u", source, inst->cid, state);
    }
}

#endif /* USE_SYNERGY */

#endif /* defined(INCLUDE_LE_AUDIO_UNICAST) */
